import { HTTPError } from "./error";
import type { Config, ConfigLock, Storage } from "./types";
import { useLocalStorage } from "@vueuse/core";
import type { Operation } from "fast-json-patch";
import { defineStore } from "pinia";
import { v4 as uuidv4 } from "uuid";

type Method = "GET" | "POST" | "PUT" | "PATCH" | "DELETE";

export const useMasterStore = defineStore("master", () => {
  const serverURL = useLocalStorage("serverURL", "");
  const token = useLocalStorage("token", "");
  const configLock = useLocalStorage("configLock", uuidv4());

  function url(path: string, query?: Record<string, string>) {
    query ??= {};
    query.token = token.value;
    query.lock = configLock.value;
    const queryString = Object.entries(query)
      .map(([k, v]) => `${k}=${v}`)
      .join("&");
    return `${serverURL.value}${path}?${queryString}`;
  }

  async function request<T>(
    method: Method,
    resource: string,
    body?: T,
    query?: Record<string, string>
  ) {
    const headers = new Headers();
    if (body !== undefined) {
      headers.set("Content-Type", "application/json");
    }
    const response = await fetch(url(resource, query), {
      method,
      headers,
      body: JSON.stringify(body),
    });
    const value = await response.json();
    if (!response.ok) {
      throw new HTTPError(value);
    }
    return value;
  }

  return {
    serverURL,
    token,
    configLock,
    resource: (key: string) => url(`/resources/${btoa(encodeURI(key))}`),
    testLogin: (): Promise<{ result: boolean }> =>
      request("GET", "/test/login"),
    getConfigLock: (): Promise<ConfigLock> => request("GET", "/config/lock"),
    putConfigLock: (): Promise<number> =>
      request("PUT", "/config/lock", configLock.value),
    getConfig: (): Promise<Config> => request("GET", "/config"),
    patchConfig: (patch: Operation[]): Promise<void> =>
      request("PATCH", "/config", patch),
    getStorageIds: (): Promise<string[]> => request("GET", "/storages"),
    getStorage: (id: string): Promise<Storage | null> =>
      request("GET", `/storages/${id}`),
    deleteStorage: (id: string): Promise<void> =>
      request("DELETE", `/storages/${id}`),
    $reset: () => {},
  };
});
